package devicestest;

public class NetDeviceClient extends javax.swing.JFrame implements java.lang.Runnable{
	private static final long serialVersionUID = 1L;

	private boolean running = false;
	private java.lang.Object lock2 = new java.lang.Object();
	private java.lang.Object lock = new java.lang.Object();
	private java.lang.Thread fil = null;

	private javax.swing.JTextField[] textFields = new javax.swing.JTextField[16];

	private int localPort = 65001;
	private int remotePort = 65000;
	private java.lang.String remoteIp = "localhost";
	private devicestest.NetDeviceClient window = this;

	public NetDeviceClient(){
		this.setSize(new java.awt.Dimension(300,530));
		this.setTitle("SEP16 External Device");
		this.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
		this.setJMenuBar(new javax.swing.JMenuBar());
		this.getJMenuBar().add(this.getConfMenu());
		java.awt.Container jp = this.getContentPane();
		jp.setLayout(new java.awt.GridLayout(16,1));
		jp.add(this.getMemoryPosition(1));
		jp.add(this.getMemoryPosition(2));
		jp.add(this.getMemoryPosition(3));
		jp.add(this.getMemoryPosition(4));
		jp.add(this.getMemoryPosition(5));
		jp.add(this.getMemoryPosition(6));
		jp.add(this.getMemoryPosition(7));
		jp.add(this.getMemoryPosition(8));
		jp.add(this.getMemoryPosition(9));
		jp.add(this.getMemoryPosition(10));
		jp.add(this.getMemoryPosition(11));
		jp.add(this.getMemoryPosition(12));
		jp.add(this.getMemoryPosition(13));
		jp.add(this.getMemoryPosition(14));
		jp.add(this.getMemoryPosition(15));
		jp.add(this.getInterruptButton());

		this.fil = new java.lang.Thread(this);
		this.fil.start();
		this.start();
		this.setVisible(true);
	}

	private javax.swing.JMenu getConfMenu(){
		javax.swing.JMenu m = new javax.swing.JMenu();
		m.setText("Conf");

		javax.swing.JMenuItem item = new javax.swing.JMenuItem("Net Conf");
		item.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(java.awt.event.ActionEvent e){
				boolean done = false;
				gui.NetConfPane netconf = new gui.NetConfPane(localPort, remotePort, remoteIp);
				Object[] options = {"OK", "Cancel"};
				while(!done){
					int n =	javax.swing.JOptionPane.showOptionDialog(window, netconf, "Net Device Configuration", javax.swing.JOptionPane.YES_NO_OPTION, javax.swing.JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
					if(n == 0){
						if(netconf.checkSanity()){
							window.localPort = netconf.getLPort();
							window.remoteIp = netconf.getRIp();
							window.remotePort = netconf.getRPort();
							window.stop();
							window.start();
							done = true;
						}else{
							window.error("Please, enter a positive value for Local and Remote Port fields");
						}
					}else{
						done = true;
					}
				}
			}
		});
		m.add(item);

		return(m);
	}

	private javax.swing.JPanel getMemoryPosition(int n){
		javax.swing.JPanel jp = new javax.swing.JPanel();
		this.textFields[n] = new javax.swing.JTextField(10);
		this.textFields[n].setText("0x0000");
		jp.add(this.textFields[n]);
		javax.swing.JButton button = new javax.swing.JButton();
		button.setText("Send " + n);
		button.addActionListener(new ButtonListener(n));
		jp.add(button);
		return(jp);
	}
	private javax.swing.JPanel getInterruptButton(){
		javax.swing.JPanel jp = new javax.swing.JPanel();
		this.textFields[0] = new javax.swing.JTextField(10);
		this.textFields[0].setText("0x0001");
		javax.swing.JButton button = new javax.swing.JButton();
		button.setText("Send Interrupt");
		button.addActionListener(new ButtonListener(0));
		jp.add(button);
		return(jp);
	}
	private class ButtonListener implements java.awt.event.ActionListener{
		int value = 0;
		public ButtonListener(int value){
			this.value = value;
		}
		public void actionPerformed(java.awt.event.ActionEvent e){
			buttonPressed(value);
		}
	}
	private void buttonPressed(int n){
		short auxPos = (short)n;
		short auxVal = 0;
		try{
			int auxValInt = java.lang.Integer.decode(this.textFields[n].getText()).intValue();
			auxVal = (short)(auxValInt & 0xFFFF);
			this.sendWord(auxPos, auxVal);
		}catch(java.lang.NumberFormatException e){
			this.error("Cannot decode \""+ this.textFields[n].getText() + "\" to a number");
		}
	}

	public void run() {
		java.net.DatagramSocket socket = null;
		java.net.DatagramPacket paquet_rebre = null;
		byte[] buffer = new byte[10];
		short[] buffer2 = new short[2];
 		while(true){
			synchronized(lock){
				while(!this.running){
					try {
						this.lock.wait();
					} catch (InterruptedException e) {}
				}
			}
			try {
				socket = new java.net.DatagramSocket(this.localPort);
				socket.setSoTimeout(500);
				paquet_rebre = new java.net.DatagramPacket(buffer,buffer.length);
				while(this.running){
					try{
						try{
							socket.receive(paquet_rebre);
							this.debug("Packet received");
							buffer = paquet_rebre.getData();
							this.debug("Buffer Length: " + buffer.length);
							if(buffer.length > 4){
								buffer2[0] = (short)((buffer[0]<<8) | (0xFF & buffer[1]));
								buffer2[1] = (short)((buffer[2]<<8) | (0xFF & buffer[3]));
								this.debug(java.lang.String.format("Received Data: 0x%04X, 0x%04X", buffer2[0], buffer2[1]));
								if(buffer2[0] == (short)0xFFFF){
									this.debug("Received an Interrupt packet\n");
								}else if(((buffer2[0] & 0xFFF0) == 0) && ((buffer2[0] & 0x000F) != 0)){
									this.textFields[buffer2[0]].setText(java.lang.String.format("0x%04X", buffer2[1]));
								}else{
									this.error("Error in received packet\n");
								}
							}
						}catch(java.net.SocketTimeoutException e){}
					}catch(java.io.IOException e){
						this.error("Error al rebre la petició: " + e.getLocalizedMessage());
					}
				}
				synchronized(lock2){
					this.lock2.notifyAll();
				}
				socket.close();
			} catch (java.net.SocketException e) {
				this.error("Error creating the socket: " + e.getLocalizedMessage());
				this.stop();
			}
		}
	}
	public void start(){
		this.running = true;
		synchronized(lock){
			this.lock.notifyAll();
		}
		this.debug("Net Device Activation");
	}
	public void stop(){
		synchronized(lock2){
			this.running = false;
			try {
				this.lock2.wait();
			} catch (InterruptedException e) {}
		}
		this.debug("Net Device DeActivation");
	}
	public void sendWord(short address, short value){
		short[] buff = new short[2];
		byte[] buff2 = new byte[4];
		buff[1] = value;
		if((address & 0xF) == 0){
			//Control address
			if((buff[1] & 0x1) == 1){
				buff[0] = (short)0xFFFF;
			}
		}else{
			buff[0] = (short)(address & 0x0F);
		}
		java.net.InetAddress ia = null; //L'adreça del server
		java.net.DatagramSocket so = null;
		java.net.DatagramPacket dp_enviar = null;
		buff2[0] = (byte)((buff[0]>>8) & 0xFF);
		buff2[1] = (byte)(buff[0] & 0xFF);
		buff2[2] = (byte)((buff[1]>>8) & 0xFF);
		buff2[3] = (byte)(buff[1] & 0xFF);
		try{
			ia = java.net.InetAddress.getByName(this.remoteIp);
			try{
				dp_enviar = new java.net.DatagramPacket(buff2, buff2.length, ia, this.remotePort);
				so = new java.net.DatagramSocket();
				so.send(dp_enviar);
				this.debug("Packet sent");
			}catch (java.io.IOException e) {
				this.error("I/O error:" + e.getLocalizedMessage());
			}
		} catch (java.net.UnknownHostException e) {
			this.error("Unknown Host: " + e.getLocalizedMessage());
		}
	}
	public void error(java.lang.String s){
		javax.swing.JOptionPane.showMessageDialog(this, s, "Error", javax.swing.JOptionPane.ERROR_MESSAGE);
	}
	public void info(java.lang.String s){
		javax.swing.JOptionPane.showMessageDialog(this, s, "Info", javax.swing.JOptionPane.INFORMATION_MESSAGE);
	}
	public void debug(java.lang.String s){
		System.out.println(s);
	}

	public static void main(java.lang.String args[]){
		new devicestest.NetDeviceClient();
	}
}
